# peppy Copyright (c) 2006-2008 Rob McMullen
# MIT Licensed; see http://peppy.flipturn.org for more info
"""Actions for aptus mode
"""

import os, random, time, math
from cStringIO import StringIO

import wx
import wx.stc

from peppy.actions import *
from peppy.actions.minibuffer import *


from aptus.palettes import all_palettes
from aptus.options import AptusState


class RedrawFractal(SelectAction):
    """Force the fractal to be redrawn
    """
    name = "Redraw"
    default_menu = ("Aptus", -100)
    key_bindings = {'default': 'R'}

    def action(self, index=-1, multiplier=1):
        self.mode.aptus.set_view()


class AptusAttributeMixin(object):
    attribute_name = None

    def getInitialValueHook(self):
        line = getattr(self.mode.aptus, self.attribute_name)
        return str(line)

    def processMinibuffer(self, minibuffer, mode, value):
        """Callback function used to change the value.
        """
        setattr(mode.aptus, self.attribute_name, value)
        mode.aptus.set_view()

class IterationLimit(AptusAttributeMixin, MinibufferAction):
    """Set the iteration limit of the fractal engine
    """
    name = "Iteration Limit..."
    default_menu = ("Aptus", -200)
    key_bindings = {'default': 'I'}
    minibuffer = IntMinibuffer
    minibuffer_label = "Iteration Limit:"
    attribute_name = 'iter_limit'

class BailoutRadius(AptusAttributeMixin, MinibufferAction):
    """Set the iteration limit of the fractal engine
    """
    name = "Bailout Radius..."
    default_menu = ("Aptus", 210)
    key_bindings = {'default': 'B'}
    minibuffer = FloatMinibuffer
    minibuffer_label = "Bailout Radius:"
    attribute_name = 'bailout'

class RotationAngle(AptusAttributeMixin, MinibufferAction):
    """Set the iteration limit of the fractal engine
    """
    name = "Angle..."
    default_menu = ("Aptus", 220)
    key_bindings = {'default': 'A'}
    minibuffer = FloatMinibuffer
    minibuffer_label = "Angle of Rotation:"
    attribute_name = 'angle'


class ChangePaletteMixin(object):
    delta = None
    
    def action(self, index=-1, multiplier=1):
        if self.delta == 0:
            self.mode.aptus.palette_index = 0
        else:
            self.mode.aptus.palette_index += self.delta
        self.mode.aptus.palette_index %= len(all_palettes)
        self.mode.aptus.palette = all_palettes[self.mode.aptus.palette_index]
        self.mode.aptus.palette_phase = 0
        self.mode.aptus.palette_scale = 1.0
        self.bitmap = None
        self.mode.aptus.set_view()

class ResetPalette(ChangePaletteMixin, SelectAction):
    """Reset to the default palette
    """
    name = "Reset Palette"
    icon = "icons/palette.png"
    default_menu = ("Palette", -300)
    key_bindings = {'default': 'C-R'}
    delta = 0

class PrevPalette(ChangePaletteMixin, SelectAction):
    """Redraw using the previous palette in the list
    """
    name = "Prev Palette"
    icon = "icons/palette-prev.png"
    default_menu = ("Palette", 301)
    key_bindings = {'default': 'S-,'}
    delta = -1

class NextPalette(ChangePaletteMixin, SelectAction):
    """Redraw using the next palette in the list
    """
    name = "Next Palette"
    icon = "icons/palette-next.png"
    default_menu = ("Palette", 302)
    key_bindings = {'default': 'S-.'}
    delta = 1


class CycleColorsMixin(object):
    delta = None
    
    def action(self, index=-1, multiplier=1):
        self.mode.aptus.palette_phase += self.delta
        self.mode.aptus.palette_phase %= len(self.mode.aptus.palette)
        self.bitmap = None
        self.mode.aptus.set_view()

class PrevColorCycle(CycleColorsMixin, SelectAction):
    """Cycle colors in the palette in reverse
    """
    name = "Prev Color in Cycle"
    icon = "icons/color-prev.png"
    default_menu = ("Palette", -310)
    key_bindings = {'default': ','}
    delta = -1

class NextColorCycle(CycleColorsMixin, SelectAction):
    """Cycle colors in the palette
    """
    name = "Next Color in Cycle"
    icon = "icons/color-next.png"
    default_menu = ("Palette", 311)
    key_bindings = {'default': '.'}
    delta = 1



class PaletteAdjustMixin(object):
    delta = None
    color_mod = None
    
    def action(self, index=-1, multiplier=1):
        self.mode.aptus.palette.adjust(**{self.color_mod:self.delta})
        self.mode.aptus.bitmap = None
        self.mode.aptus.set_view()

class AdjustHueUp(PaletteAdjustMixin, SelectAction):
    """Adjust the hue of the palette
    """
    name = "Adjust Hue Up"
    icon = "icons/hue-up.png"
    default_menu = ("Palette", -320)
    key_bindings = {'default': ']'}
    delta = 10
    color_mod = 'hue'

class AdjustHueUpTiny(PaletteAdjustMixin, SelectAction):
    """Adjust the hue of the palette a small amount
    """
    name = "Adjust Hue Up a Tiny Bit"
    default_menu = ("Palette", 321)
    key_bindings = {'default': 'C-]'}
    delta = 1
    color_mod = 'hue'

class AdjustHueDownTiny(PaletteAdjustMixin, SelectAction):
    """Adjust the hue of the palette a small amount
    """
    name = "Adjust Hue Down a Tiny Bit"
    default_menu = ("Palette", 322)
    key_bindings = {'default': 'C-['}
    delta = -1
    color_mod = 'hue'

class AdjustHueDown(PaletteAdjustMixin, SelectAction):
    """Adjust the hue of the palette
    """
    name = "Adjust Hue Down"
    icon = "icons/hue-down.png"
    default_menu = ("Palette", 323)
    key_bindings = {'default': '['}
    delta = -10
    color_mod = 'hue'

class AdjustSaturationUp(PaletteAdjustMixin, SelectAction):
    """Adjust the saturation of the palette
    """
    name = "Adjust Saturation Up"
    icon = "icons/saturation-up.png"
    default_menu = ("Palette", -330)
    key_bindings = {'default': 'S-]'}
    delta = 10
    color_mod = 'saturation'

class AdjustSaturationUpTiny(PaletteAdjustMixin, SelectAction):
    """Adjust the saturation of the palette a small amount
    """
    name = "Adjust Saturation Up a Tiny Bit"
    default_menu = ("Palette", 331)
    key_bindings = {'default': 'S-C-]'}
    delta = 1
    color_mod = 'saturation'

class AdjustSaturationDownTiny(PaletteAdjustMixin, SelectAction):
    """Adjust the saturation of the palette a small amount
    """
    name = "Adjust Saturation Down a Tiny Bit"
    default_menu = ("Palette", 332)
    key_bindings = {'default': 'S-C-['}
    delta = -1
    color_mod = 'saturation'

class AdjustSaturationDown(PaletteAdjustMixin, SelectAction):
    """Adjust the saturation of the palette
    """
    name = "Adjust Saturation Down"
    icon = "icons/saturation-down.png"
    default_menu = ("Palette", 333)
    key_bindings = {'default': 'S-['}
    delta = -10
    color_mod = 'saturation'


class PaletteScaleMixin(object):
    factor = None
    
    def isEnabled(self):
        return self.mode.aptus.continuous
    
    def action(self, index=-1, multiplier=1):
        self.mode.aptus.palette_scale *= self.factor
        self.mode.aptus.bitmap = None
        self.mode.aptus.set_view()



class ContinuousColors(ToggleAction):
    """Set the iteration limit of the fractal engine
    """
    name = "Continuous Colors"
    default_menu = ("Palette", -400)
    key_bindings = {'default': 'C'}
    
    def isChecked(self):
        return self.mode.aptus.continuous
    
    def action(self, index=-1, multiplier=1):
        self.mode.aptus.continuous = not self.mode.aptus.continuous
        self.mode.aptus.set_view()


class StretchPalette10(PaletteScaleMixin, SelectAction):
    """Stretch the palette 10%
    """
    name = "Stretch Palette 10%"
    default_menu = ("Palette", 440)
    key_bindings = {'default': ';'}
    factor = 1.1

class StretchPalette1(PaletteScaleMixin, SelectAction):
    """Stretch the palette 1%
    """
    name = "Stretch Palette 1%"
    default_menu = ("Palette", 441)
    key_bindings = {'default': 'C-;'}
    factor = 1.01
        

class CompressPalette1(PaletteScaleMixin, SelectAction):
    """Stretch the palette 1%
    """
    name = "Compress Palette 1%"
    default_menu = ("Palette", 442)
    key_bindings = {'default': 'C-\''}
    factor = 1.0/1.01

class CompressPalette10(PaletteScaleMixin, SelectAction):
    """Stretch the palette 10%
    """
    name = "Compress Palette 10%"
    default_menu = ("Palette", 443)
    key_bindings = {'default': '\''}
    factor = 1.0/1.1


class ExportAsPNG(SelectAction):
    """Export the current image as a PNG file
    """
    name = "as PNG"
    default_menu = ("File/Export", -100)

    def action(self, index=-1, multiplier=1):
        if not self.mode.aptus.bitmap:
            return
        
        filename = self.frame.showSaveAs("Save Image as PNG", extra=".png")
        if filename:
            if filename.lower().endswith(".png"):
                self.mode.aptus.bitmap.SaveFile(filename, wx.BITMAP_TYPE_PNG)
            else:
                self.mode.aptus.setStatusText("Unrecognized file format %s" % filename)


def actions():
    return [RedrawFractal,
            
            ContinuousColors, NextPalette, PrevPalette, ResetPalette,
            
            NextColorCycle, PrevColorCycle,
            
            AdjustHueUp, AdjustHueDown, AdjustHueUpTiny,
            AdjustHueDownTiny,
            
            AdjustSaturationUp, AdjustSaturationDown,
            AdjustSaturationUpTiny, AdjustSaturationDownTiny,
            
            StretchPalette10, StretchPalette1, CompressPalette1,
            CompressPalette10,
            
            IterationLimit, BailoutRadius, RotationAngle,
            
            ExportAsPNG,
            ]
